package cn.xydpeng.springframework.context.support;

import cn.xydpeng.springframework.beans.BeansException;
import cn.xydpeng.springframework.beans.factory.BeanFactory;
import cn.xydpeng.springframework.beans.factory.config.BeanFactoryPostProcessor;
import cn.xydpeng.springframework.beans.factory.config.BeanPostProcessor;
import cn.xydpeng.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import cn.xydpeng.springframework.context.ApplicationContext;
import cn.xydpeng.springframework.context.ApplicationEvent;
import cn.xydpeng.springframework.context.ApplicationListener;
import cn.xydpeng.springframework.context.ConfigurableApplicationContext;
import cn.xydpeng.springframework.context.event.ApplicationEventMulticaster;
import cn.xydpeng.springframework.context.event.ContextRefreshedEvent;
import cn.xydpeng.springframework.context.event.SimpleApplicationEventMulticaster;
import cn.xydpeng.springframework.core.io.DefaultResourceLoader;
import cn.xydpeng.springframework.lang.Nullable;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author xydpeng
 * @version v1.0
 * @date: 2022/10/17
 * @description:
 */
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
    /**
     * Synchronization monitor for the "refresh" and "destroy".
     */
    private final Object startupShutdownMonitor = new Object();
    /**
     * Parent context.
     */
    @Nullable
    private ApplicationContext parent;
    /**
     * 钩子
     */
    private Thread shutdownHook;
    /**
     * BeanFactoryPostProcessors to apply on refresh.---存放BeanFactoryPostProcessor实例
     */
    private final List<BeanFactoryPostProcessor> beanFactoryPostProcessors = new ArrayList<>();
    @Nullable
    private ApplicationEventMulticaster applicationEventMulticaster;
    public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
    /**
     * Statically specified listeners.
     */
    private final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();

    public AbstractApplicationContext() {
    }

    public AbstractApplicationContext(ApplicationContext parent) {
        this();
        setParent(parent);
    }

    /**
     * spring启动阶段核心流程，由对应ApplicationContext构造函数触发
     * 通用模板方法，细节留给子类实现
     *
     * @throws BeansException
     * @throws IllegalStateException
     */
    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {

            //1. Tell the subclass to refresh the internal bean factory. ---创建内部beanFactory实例，并加载 BeanDefinition
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context. --- 提前配置下beanFactory
            prepareBeanFactory(beanFactory);

            //2. Invoke factory processors registered as beans in the context.
            // ---在容器实例化Bean之前，执行 BeanFactoryPostProcessor扩展点，可修改beanDefinition信息:
            //      其中会优先处理BeanFactoryPostProcessor的子类型BeanDefinitionRegistryPostProcessor;
            invokeBeanFactoryPostProcessors(beanFactory);

            //3. Register bean processors that intercept bean creation.  --- 注册BeanPostProcessor扩展点（在实例化beans时会被调用）
            registerBeanPostProcessors(beanFactory);

            //4. Initialize message source for this context. ---国际化支持
            //initMessageSource();

            //5. Initialize event multicaster for this context. --- 初始化事件发布传播器(观察者模式---):默认会new一个SimpleApplicationEventMulticaster实例
            initApplicationEventMulticaster();

            //6. Initialize other special beans in specific context subclasses. --- 实例化其他特殊的bean
            //onRefresh();

            //7. Check for listener beans and register them. ----注册事件监听器(观察者模式)
            registerListeners();

            //8. Instantiate all remaining (non-lazy-init) singletons. --- 实例化所有单例bean (内部通过触发getBean()方法)
            finishBeanFactoryInitialization(beanFactory);

            //9. Last step: publish corresponding event. --- 发布事件
            finishRefresh();
        }
    }

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {

        refreshBeanFactory();

        return getBeanFactory();
    }

    protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // Configure the bean factory with context callbacks. --- 为ApplicationContextAware 回调做准备
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    }

    protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        List<BeanFactoryPostProcessor> beanFactoryPostProcessors = getBeanFactoryPostProcessors(); //为空

        //TODO 处理BeanDefinitionRegistryPostProcessor

        String[] beanNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class);
        for (String beanName : beanNames) {
            BeanFactoryPostProcessor beanFactoryPostProcessor = beanFactory.getBean(beanName, BeanFactoryPostProcessor.class);
            beanFactoryPostProcessors.add(beanFactoryPostProcessor);
        }

        //TODO 1、细分BeanFactoryPostProcessor类型，优先处理BeanDefinitionRegistryPostProcessors， 2、 ordered优先级 ......

        for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
            postProcessor.postProcessBeanFactory(beanFactory);
        }
    }

    protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        String[] beanNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class);
        //TODO BeanPostProcessor分类、排序等逻辑

        for (String ppName : beanNames) {
            BeanPostProcessor processor = beanFactory.getBean(ppName, BeanPostProcessor.class);
            beanFactory.addBeanPostProcessor(processor);
        }
    }

    /**
     * Finish the initialization of this context's bean factory,
     * initializing all remaining singleton beans.
     */
    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        //TODO 其他逻辑

        // Instantiate all remaining (non-lazy-init) singletons.
        beanFactory.preInstantiateSingletons();
    }


    public List<BeanFactoryPostProcessor> getBeanFactoryPostProcessors() {
        return beanFactoryPostProcessors;
    }

    @Override
    public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor) {
        this.beanFactoryPostProcessors.add(postProcessor);
    }

    /**
     * Subclasses must implement this method to perform the actual configuration load.
     */
    protected abstract void refreshBeanFactory() throws BeansException;

    @Override
    public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

    @Override
    public void registerShutdownHook() {
        if (this.shutdownHook == null) {
            this.shutdownHook = new Thread(() -> {
                synchronized (this.startupShutdownMonitor) {
                    doClose();
                }
            }, "SpringContextShutdownHook");
            Runtime.getRuntime().addShutdownHook(this.shutdownHook);
        }
    }

    @Override
    public void setParent(@Nullable ApplicationContext parent) {
        this.parent = parent;
    }

    protected void doClose() {
        //TODO
        //1. Publish shutdown event.
        //publishEvent(new ContextClosedEvent(this));

        //2. Destroy all cached singletons in the context's BeanFactory. --- 调用bean的destroy方法
        destroyBeans();
    }

    @Override
    public void close() {
        synchronized (this.startupShutdownMonitor) {
            doClose();
            // If we registered a JVM shutdown hook, we don't need it anymore now:
            // We've already explicitly closed the context.
            if (this.shutdownHook != null) {
                try {
                    Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
                } catch (IllegalStateException ex) {
                    // ignore - VM is already shutting down
                }
            }
        }
    }


    // Implementation of BeanFactory interface

    @Override
    public Object getBean(String name) throws BeansException {
        return getBeanFactory().getBean(name);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        return getBeanFactory();
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return getBeanFactory().getBean(name, requiredType);
    }


    // Implementation of ListableBeanFactory interface

    @Override
    public String[] getBeanNamesForType(Class<?> type) {
        return getBeanFactory().getBeanNamesForType(type);
    }

    @Override
    public String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType) {
        return getBeanFactory().getBeanNamesForAnnotation(annotationType);
    }

    @Override
    public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
        return getBeanFactory().getBeansOfType(type);
    }

    @Override
    public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException {
        return getBeanFactory().getBeansWithAnnotation(annotationType);
    }


    // Implementation of HierarchicalBeanFactory interface

    @Override
    public BeanFactory getParentBeanFactory() {
        return this.parent;
    }

    @Override
    public boolean containsLocalBean(String name) {
        return getBeanFactory().containsLocalBean(name);
    }

    protected void destroyBeans() {
        getBeanFactory().destroySingletons();
    }

    protected void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
            this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
        } else {
            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster();
            beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
        }
    }

    protected void registerListeners() {
        for (ApplicationListener listener : getApplicationListeners()) {
            this.applicationEventMulticaster.addApplicationListener(listener);
        }
        //TODO 注意源码中： Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let post-processors apply to them!
        String[] beanNames = getBeanNamesForType(ApplicationListener.class);
        for (String beanName : beanNames) {
            ApplicationListener listener = getBean(beanName, ApplicationListener.class);
            this.applicationEventMulticaster.addApplicationListener(listener);
        }
    }

    public Set<ApplicationListener<?>> getApplicationListeners() {
        return applicationListeners;
    }

    @Override
    public void publishEvent(Object event) {
        //TODO
        ApplicationEvent applicationEvent = (ApplicationEvent) event;
        this.applicationEventMulticaster.multicastEvent(applicationEvent);
    }

    protected void finishRefresh(){

        publishEvent(new ContextRefreshedEvent(this));
    }
}
